/**
 *   该组件集成了顶部查询,分页表格,自定义列,查询,详情功能
 *   考虑到下拉框级联的问题,因为级联的数据可能多样,因此不使用级联下拉框组件,而是通过多个下拉框手动实现.在使用级联时,父级级联名称和子级联名称
 *   在使用级联时,父级联需要在selectOpt中定义subProp字段来指定子级联,
 *   子级联需要在selectOpt中定义fun来指定数据获取方式,parentProp来作为父级联值的参数名(默认取父prop)
 *
 *   2023.8.30
 *   将selectOpt属性中的url改为fun,值为返回对象为下拉项(数组),该方法可以是同步方法也可以是异步方法
 *   fun实质上是对data扩展,针对于异步方法,以车辆的下拉框为例:
 *   selectOpt:{
 *     fun: () => {
 *     // 返回一个Promise对象
 *      return new Promise((resolve,reject )=> {
 *        // 异步方法,当然也可以使用非异步的方法.
 *        this.$rq.list(trainApi.url).then(res => {
 *          // 这里可以再添加一些数据处理...
 *          // 数据获取成功后返回
 *          resolve(res.data)
 *        },error => {
 *          // 如果失败,返回一个空数组
 *          resolve([])
 *          // 或者直接reject也可以
 *          // reject()
 *        })
 *      }
 *     })
 *     labelField: 'name',
       valueField: 'id',
 *   }
 *
 *
 *   2023.11.20
 *   关于权限按钮: 通过插槽的形式自行实现,新增了行上按钮插槽"buttons",加上原有的行内操作按钮operations,这样就实现了自定义的按钮.
 *   不管是行上还是行内的按钮,现在的统一逻辑是将插槽和行内按钮进行合并
 *
 *   2023.12.1
 *   经排查,el-table-column 中添加fixed属性会导致该列渲染两次,该问题初步确认为element-ui自身的性能优化问题,暂不解决.但如果数据量较大(例如菜单管理),其渲染的时长差距在一倍(选中某行所花的时间分别是400毫秒和200毫秒)
 *
 *   2023.12.12
 *   表格的排序功能实现,现在只需要在对应的column下添加属性sortable: true即可实现排序,分页条件中会自动拼接sort和orderBy
 *
 *   2023.12.28
 *   selectOpt.fun现在支持非异步方法了!
 *
 *   2024.1.3
 *   selectOpt.fun现在也支持promise了!
 *
 *   2024.4.24
 *   增加了异步数据获取完成的钩子,数据加载完成后会调用父组件的pageReady方法.
 *
 *   2024.9.18
 *   修改了defaultSort的BUG,在tableConf中添加defaultSort属性即可设置默认排序: 例如{prop: 'createTime', order: 'descending'},即根据username进行倒序
 *
 */
import utils from '@/common/utils.js'
import {mapGetters} from 'vuex'
import cache from '@/cache'

const SORT = {
  'descending' : 'desc',
  'ascending' : 'asc'
}
// 表格默认选项
const defaultTableConf = {
  // 默认多选表格
  multiple: true,
  // 列属性
  columns: [],
  // 显示不显示序号
  index: false,
  // 操作列宽度
  operateWidth: '150px',
  // 默认不展开行
  expand: false,
  // 默认有操作列
  operation: true,
  // 默认分页...
  paging: true,
  // 默认不展开(树结构前提下)
  defaultExpandAll: false,
  // 默认排序,同el-table的default-sort属性 例如{prop: 'createTime', order: 'descending'},即根据username进行倒序
  // 默认排序,同el-table的default-sort属性 例如{prop: 'createTime', order: 'ascending'},即根据username进行正序
  defaultSort: {}
}

const defaultSearchConf = {
  // 类型,默认输入框
  type: 'input',
  // 占位符
  placeholder: '',
  // 是否允许为空.如果不允许为空,那么该组件不可清空,默认选中第一项(仅下拉框实现此功能,其他组件请使用插槽实现)
  notEmpty: false
}

// 默认下拉配置
const defaultSelectOpt = {
  // 可清除
  clearable: true,
  // 可多选
  multiple: false,
  // value属性名
  valueField: 'value',
  // label属性名
  labelField: 'label',
  data: [],
}
export default {
  name: "ePage",
// 不支持通过改变prop的值来控制显示
  props: {
    //是否不展开查询条件
    isCollapseP:  {
      required: false,
      type: Boolean,
      default() {
        return true
      }
    },
    // 表格配置
    tableConf: {
      required: true,
      type: Object
    },
    // 查询项配置
    searchConf: {
      required: true,
      type: Array
    },
    // 按钮
    buttons: {
      required: false,
      default() {
        return []
      }
    },
    // 链接
    links: {
      required: false,
      default() {
        return []
      }
    },
    rowKey: {
      required: false,
      type: String,
      default: 'id'
    }
  },
  computed: {
    ...mapGetters('permission', [
      'activeRouteCode',
    ])
  },
  watch: {
    searchConf: {
      handler(value) {

      },
      deep: true
    }
  },
  data() {
    return {
      // 表格数据
      data: [],
      // 表格等待
      loading: true,
      // 需要显示的按钮
      rButtons: [],
      // 需要显示的link
      rLinks: [],
      // 所有子级联项,子级联项不需要初始化下拉项
      subProps: [],
      // 展开更多
      isCollapse: true,
      // 处理后的查询项
      rSearchConf: [],
      // 自定义查询筛选后的实际查询项
      realSearchConf: {},
      // 处理后的查询项
      rTableConf: {},
      realTableConf: {},
      // 查询项
      searchData: {},
      sorting: {
        orderBy: '',
        sort: ''
      },
      // 分页
      pagination: {
        total: 0,
        pageOn: 1,
        pageSize: 10,
      },
      multiSelection: [],
      customConfVisible: false,
      // 自定义列/查询相关...
      // 实际显示的表格项
      realTableCol: [],
      // 所有的表格项
      tableCol: [],
      // 实际显示的查询项
      realSearchCol: [],
      // 所有的查询项
      searchCol: [],
      // 点击的是否是[自定义查询]按钮
      isCustomSearch: false,
      tmpSearchCol: [],
      tmpTableCol: [],
      // 自定义配置
      customConf: ''
    }
  },
  methods: {
    // 初始化
    init() {
      /**
       * 关于初始化与自定义查询项的兼容: 初始化时会将所有的查询项都进行初始化(即rSearchConf),自定义查询仅显示rSearchConf中的部分内容(即realSearchConf)
       */
      this.initOpt()
      this.initButtons()
      this.initBinding()
      this.initData()
      this.initCustomConf()
      this.initSort()
    },
    // 初始化绑定,查询项在双向绑定过程中,searchData属性为空,因此需要进行$set防止双向绑定失效
    initBinding() {
      this.rSearchConf.forEach(e => {
        // 为了避免el-input-number初始显示值为0,这里绑定为undefined而不是空串
        this.$set(this.searchData, e.prop, undefined)
      })
    },
    // 初始化排序
    initSort(){
      if(this.realTableConf.defaultSort){
        if(this.realTableConf.defaultSort.prop && this.realTableConf.defaultSort.order){
          this.sorting.orderBy = utils.to_snake_case(this.realTableConf.defaultSort.prop)
          this.sorting.sort = SORT[this.realTableConf.defaultSort.order]
        }
      }
    },
    // 初始化默认配置
    initOpt() {
      // 初始化查询配置
      this.isCollapse = this.isCollapseP;
      this.rSearchConf = []
      this.searchConf.forEach(e => {
        let tmp = {}
        tmp = {...defaultSearchConf, ...e}
        if (tmp.type == 'select') {
          if (tmp.selectOpt.subProp) { // 获取所有的子级联项
            try {
              // 尝试转换成数组
              this.subProps.push(...JSON.parse(tmp.selectOpt.subProp))
            } catch (err) {
              // 不能转换成数组那么就把整个subProp放进去...
              this.subProps.push(tmp.selectOpt.subProp)
            }
          }
          // 初始化下拉配置
          tmp.selectOpt = {...defaultSelectOpt, ...e.selectOpt}
        }
        this.rSearchConf.push(tmp)
      })
      // 初始化表格配置
      this.rTableConf = {...defaultTableConf, ...this.tableConf}
    },
    setSelectOptData(data, searchConf) {
      searchConf.selectOpt.data = []
      // data 必须是数组
      if (data instanceof Array) {
        data.forEach(e => {
          let tmp = {}
          tmp[searchConf.selectOpt.labelField] = e[searchConf.selectOpt.labelField]
          tmp[searchConf.selectOpt.valueField] = e[searchConf.selectOpt.valueField]
          searchConf.selectOpt.data.push(tmp)
        })
      }
    },
    // 初始化数据
    initData() {
      let promises = []
      let promiseObjs = []
      this.rSearchConf.forEach(e => {
        if (e.type == 'select' && e.selectOpt.fun && this.subProps.indexOf(e.prop) == -1) { // 需要异步获取数据的,且不是子级联的下拉框 进行初始化
          e.selectOpt.data = []
          let fun = e.selectOpt.fun
          // 如果是个异步方法
          if (fun.then) {
            promises.push(fun)
            promiseObjs.push(e)
            // fun.then(data => {
            //   this.setSelectOptData(data, e)
            // }, err => {
            //   console.error(err)
            // })
          } else if (fun instanceof Function) {
            let res = fun()
            if (res.then) {
              promises.push(res)
              promiseObjs.push(e)
              // res.then(data => {
              //   this.setSelectOptData(data, e)
              // }, err => {
              //   console.error(err)
              // })
            } else {
              this.setSelectOptData(res, e)
            }
          }
        }
      })
      if (promises.length) {
        Promise.all(promises).then(res => {
          res.forEach((item, index) => {
            this.setSelectOptData(item, promiseObjs[index])
            this.$nextTick(_ => {
              this.$parent.$options.methods && this.$parent.$options.methods.pageReady && this.$parent.$options.methods.pageReady.call(this.$parent, this.searchData, this.rSearchConf, this)
            })
          })
        }).catch(err => {
          console.error(err)
        })
      } else {
        this.$nextTick(_ => {
          this.$parent.$options.methods && this.$parent.$options.methods.pageReady && this.$parent.$options.methods.pageReady.call(this.$parent, this.searchData, this.rSearchConf, this)
        })
      }
    },
    // 展开/收起按钮
    handleCollapse() {
      this.isCollapse = !this.isCollapse
      this.$nextTick(() => this.$refs.table.doLayout())
    },
    // 初始化按钮配置,这里仅考虑非权限按钮
    initButtons() {
      this.buttons.forEach(e => {
        if (e.isLink) {
          this.rLinks.push(e)
        } else {
          this.rButtons.push(e)
        }
      })
    },
    // 初始化自定义列/查询配置
    initCustomConf() {
      this.getCustomConf()
      this.setCustomConf()
    },
    setCustomConf() {
      this.realTableConf = {...this.rTableConf}
      this.realTableConf.columns = this.rTableConf.columns.filter(e => this.realTableCol.indexOf(e.prop) > -1)
      this.realSearchConf = this.rSearchConf.filter(e => this.realSearchCol.indexOf(e.prop) > -1)
    },
    getCustomConf() {
      // 获取所有的查询项
      this.searchCol = this.rSearchConf.map(e => {
        return {label: e.label, key: e.prop}
      })
      // 获取所有的列表项
      this.tableCol = this.rTableConf.columns.map(e => {
        return {label: e.label, key: e.prop}
      })
      this.realSearchCol = this.searchCol.map(e => e.key)

      this.realTableCol = this.tableCol.map(e => e.key)

      // 获取缓存中的自定义信息
      this.customConf = cache.getCustomColumn()
      // 判断当前用户的当前路由是否已经配置
      if (this.customConf) {
        if (this.customConf[this.currentUserName]) {
          if (this.customConf[this.currentUserName][this.activeRouteCode]) {
            this.realSearchCol = this.customConf[this.currentUserName][this.activeRouteCode].searchCol
            this.realTableCol = this.customConf[this.currentUserName][this.activeRouteCode].tableCol
          } else {
            this.customConf[this.currentUserName][this.activeRouteCode] = {
              tableCol: this.realTableCol,
              searchCol: this.realSearchCol
            }
          }
        } else {
          let tmp = {}
          tmp[this.activeRouteCode] = {tableCol: this.realTableCol, searchCol: this.realSearchCol}
          this.customConf[this.currentUserName] = tmp
        }
      } else {
        let tmp = {}
        tmp[this.currentUserName] = {}
        tmp[this.currentUserName][this.activeRouteCode] = {tableCol: this.realTableCol, searchCol: this.realSearchCol}
        this.customConf = tmp
      }
    },

    // ******************************************自定义列/查询方法****
    // 自定义查询
    handleCustomSearch() {
      this.isCustomSearch = true
      this.tmpSearchCol = [...this.realSearchCol]
      this.tmpTableCol = [...this.realTableCol]
      this.customConfVisible = true
    },
    // 自定义列
    handleCustomColumn() {
      this.isCustomSearch = false
      this.tmpTableCol = [...this.realTableCol]
      this.tmpSearchCol = [...this.realSearchCol]
      this.customConfVisible = true
    },
    // 自定义列/查询保存事件
    customConfConfirm() {
      if (!this.tmpTableCol.length || !this.tmpSearchCol.length) {
        this.$message.warning('请至少选择一项')
        return
      }
      this.customConfVisible = false
      this.customConfSave()
    },
    getSearchData() {
      return this.searchData
    },
    // 保存方法
    customConfSave() {
      this.realTableCol = this.tmpTableCol
      this.realSearchCol = this.tmpSearchCol
      this.customConf[this.currentUserName][this.activeRouteCode] = {
        searchCol: this.realSearchCol,
        tableCol: this.realTableCol
      }
      this.storage.setCustomColumn(this.customConf)
      this.setCustomConf()
    },
    // 自定义列/查询关闭事件
    handleCustomConfClose() {
      this.customConfVisible = false
    },
    // button/link点击事件
    handleButtonClick(event, row, column, $index) {
      this.$emit('event', event, {selection: this.multiSelection, row, column, $index})
    },
    // 下拉框变化事件
    handleSelectorChange(prop, subProp, selector) {
      if (subProp) { // 判断是否是父级联,如果是父级联,那么在变化时需要将子级联的下拉项均更新,并且判断子级联的值是否有效
        let _subProp = null
        try {
          _subProp = JSON.parse(subProp)
        } catch (err) {
          _subProp = [subProp]
        }
        this.realSearchConf.forEach(e => {        // 查找它的子级联
          if (~_subProp.indexOf(e.prop)) { // 父级联无需进行处理,以下均是对子级联的处理......
            // 当父值变化后,子下拉项一定需要重新获取...
            e.selectOpt.data = []
            if (this.searchData[prop]) { // 如果父值不为空,那么尝试异步获取下拉值
              let params = {}
              // 子级联的传参为他的父属性值与其params属性合并后的值
              params = {...e.selectOpt.params}
              params[e.selectOpt.parentProp || prop] = this.searchData[prop]
              if (!e.selectOpt.fun) {
                console.error(`${e.prop}的fun不存在!`)
                this.searchData[e.prop] = ''
                e.selectOpt.data = []
                return
              }

              let res = e.selectOpt.fun(params)
              // 判断是不是promise
              if (res.then) {
                res.then(data => {
                  this.setSelectOptData(data, e)
                  // 下拉项获取成功后,需要判断子值当前选中项是否有效......
                  let flag = false // 判断子值当前选中项是否存在下拉值中
                  e.selectOpt.data.forEach(e1 => {
                    if (e1[e.selectOpt.valueField] == this.searchData[e.prop]) {
                      flag = true
                    }
                  })
                  if (!flag) { // 无效下拉选...
                    this.searchData[e.prop] = ''
                  }
                }, error => {
                  e.selectOpt.data = []
                })
              } else if (Array.isArray(res)) {
                this.setSelectOptData(res, e)
                // 下拉项获取成功后,需要判断子值当前选中项是否有效......
                let flag = false // 判断子值当前选中项是否存在下拉值中
                e.selectOpt.data.forEach(e1 => {
                  if (e1[e.selectOpt.valueField] == this.searchData[e.prop]) {
                    flag = true
                  }
                })
                if (!flag) { // 无效下拉选...
                  this.searchData[e.prop] = ''
                }
              } else {
                this.searchData[e.prop] = ''
                this.setSelectOptData([], e)
              }
            } else { // 如果父级联值为空,那么子级联一定为空
              this.searchData[e.prop] = ''
              this.setSelectOptData([], e)
            }
          }
        })
      }
    },
    handleSelectorClick(prop) {
      this.rSearchConf.forEach(conf => {
        if (conf.selectOpt && conf.selectOpt.subProp) {
          if (~conf.selectOpt.subProp.indexOf(prop) && !this.searchData[conf.prop]) {
            this.$message.warning(`请先选择${conf.label}`)
          }
        }
      })
    },
    // 获取表格数据
    getData(action = 'page') {

      // 如果是点击查询按钮或者重置按钮,那么重置页码
      if (['page', 'reset'].includes(action)) {
        this.pagination.pageOn = 1
      }
      this.loading = true
      let pagination = {}
      if (this.realTableConf.paging) {
        pagination.pageSize = this.pagination.pageSize
        pagination.pageOn = this.pagination.pageOn
      }
      // 如果是重置操作,那么需要将所有级联相关的元素的子元素内容重置
      // 还需要还原默认的查询条件
      if (action === 'reset') {
        this.realSearchConf.filter(conf => ~this.subProps.indexOf(conf.prop)).forEach(conf => {
          conf.selectOpt.data = []
        })
        this.initSort()
      }
      // 调用父方法,回传查询项以及分页数据.
      this.$emit('getData', utils.formatObj({
          ...this.searchData,
          ...this.sorting,
          ...pagination
        }), (payload) => {
          this.loading = false
          this.pagination.total = payload.total
          this.data = payload.data || []
          // 该功能暂时注释掉,无用.
          // if(payload.tableConf){
          //   Object.assign(this.realTableConf, payload.tableConf)
          // }
          // this.$nextTick(()=>{
          //   this.$refs.table.doLayout()
          // })
        },
        action)
    },
    // 重置方法
    reset() {
      // if (this.$refs.searchForm) {
      // this.$refs.searchForm.clearValidate()
      // this.$refs.searchForm.resetFields()
      Object.keys(this.searchData).forEach(e => {
        this.searchData[e] = ''
      })
      // }
      this.getData('reset')
    },
    // ******************************************表格组件方法**
    // 筛选
    handleSortChange({prop, order}) {
      let orderBy = utils.to_snake_case(prop)
      switch (order) {
        case 'ascending':
          this.sorting.orderBy = orderBy
          this.sorting.sort = SORT['ascending']
          break
        case 'descending':
          this.sorting.orderBy = orderBy
          this.sorting.sort = SORT['descending']
          break
        // 这里其实是null
        default:
          this.sorting.orderBy = null
          this.sorting.sort = null
      }
      this.getData('sort')
    },
    // 展开方法
    handleExpandChange(row, expandedRows) {
      // 判断状态是展开还是关闭,由于当前element-ui版本没有支持,那么需要自行判断
      // 根据row, expandedRows之间的关系,如果expandedRows中不包含row,那么是收起的,否则认为是展开
      let isExpand = true
      if (this.rowKey && row[this.rowKey]) {
        if (!~expandedRows.findIndex(expandedRow => expandedRow[this.rowKey] === row[this.rowKey])) {
          isExpand = false
        }
      }
      this.$emit('expandChange', row, isExpand, expandedRows)
    },
    // 单击改变选中状态
    handleRowClick(row) {
      this.$refs.table.toggleRowSelection(row)
    },
    // 双击查看详情
    handleDbclick(row) {
      this.$emit('event', 'detail', {row})
    },
    // 多选框勾选变化
    handleSelectionChange(val) {
      this.multiSelection = val
    },
    // ******************************************分页组件方法**
    handleSizeChange() {
      this.getData('sizeChange')
    },
    currentChange() {
      this.getData('currentChange')
    },
    prev() {
    },
    next() {
    },
    getQuery(){
      return this.searchData
    },
    // 阻止冒泡
    stopPropagation(){
    }
  },
  created() {
    this.init()
  },
  mounted() {
    // 对 Table 进行重新布局.避免出现错行的问题
    this.$nextTick(() => {
      this.$refs.table.doLayout()
    })
  },
  // 作为keepalive缓存的组件,当组件被插入到dom时调用
  activated() {
    this.$refs.table.doLayout()
  }
}
